/////////// VARIABLES

float4x4 World;
float4x4 WorldRotation;
float4x4 View;
float4x4 Projection;
float4x4 NormalMatrix;
float4x4 WVP;
float3 CameraPosition;
//Volymetric light
float3 SunPosition;
float Density = 1;
float Exposure = 1;
float Weight = 1;
float Decay = 1;

uniform extern texture DepthTexture;
uniform extern texture SSAOTexture;
uniform extern texture TempTexture;
uniform extern texture Texture;
uniform extern texture DisplacementTexture;
uniform extern texture Noise;

float Alpha = 1.0;
float Thickness = 4.0;

float3 Ambient = float3(0.2,0.2,0.2);

float4 DiffuseColor;
float4 SpecularColor;
float4 EmissiveColor;


float Light1Intensity;
float3 Light1Position;
float3 Light1Target;
float3 Light1Direction;
float3 Light1Color;
float3 Light1SpecularColor;
float Light1SpecularIntensity;
float Light1SpecularGloss;

float Light2Intensity;
float3 Light2Position;
float3 Light2Target;
float3 Light2Direction;
float3 Light2Color;
float3 Light2SpecularColor;
float Light2SpecularIntensity;
float Light2SpecularGloss;

float Time;
float Grow = 0.5;

float Fogginess = 0;
float4 FogColor = float4(1,1,1,1);

float GeggStep = 0;

float DisplacementAmount = 30;


//////////// SAMPLERS

sampler DepthTextureSampler = sampler_state
{
	Texture = <DepthTexture>;
	MipFilter = None;
    MinFilter = Point;
    MagFilter = Point;
    AddressU  = Clamp;
    AddressV  = Clamp;
};

sampler SSAOTextureSampler = sampler_state
{
	Texture = <SSAOTexture>;
	MipFilter = None;
    MinFilter = Point;
    MagFilter = Point;
    AddressU  = Clamp;
    AddressV  = Clamp;
};


sampler TempTextureSampler = sampler_state
{
	Texture = <TempTexture>;
	MipFilter = Linear;
    MinFilter = Linear;
    MagFilter = Linear;
    AddressU  = Wrap;
    AddressV  = Wrap;
};

sampler TextureSampler = sampler_state
{
	Texture = <Texture>;
	MipFilter = Linear;
    MinFilter = Linear;
    MagFilter = Linear;
    AddressU  = Wrap;
    AddressV  = Wrap;
};


sampler DisplacementTextureSampler = sampler_state
{
	Texture = <DisplacementTexture>;
	MipFilter = Linear;
    MinFilter = Linear;
    MagFilter = Linear;
    AddressU  = Clamp;
    AddressV  = Clamp;
};

sampler TextureSamplerNoise = sampler_state
{
	Texture = <Noise>;
	MipFilter = None;
    MinFilter = Point;
    MagFilter = Point;
    AddressU  = Wrap;
    AddressV  = Wrap;
};



/////////// INPUTS AND OUTPUTS

struct VertexShaderInput
{    
    float4 Position : POSITION0;
    float4 Normal : NORMAL0;
    float2 TexCoord : TEXCOORD0;
	float4 Color : COLOR0;
};

struct VertexShaderOutput
{
	float4 SunkPosition : POSITION;
    float4 Position : POSITION1;
	float4 PositionWorld : POSITION2;
	float4 Normal : NORMAL0;
	float4 NormalEye : NORMAL1;
    float2 TexCoord : TEXCOORD0; 
    float4 Color : COLOR0;
    
    float3 ShadowCoord : TEXCOORD3;    
    float3 RealDistance: TEXCOORD4;
};

struct VertexShaderInputCombine
{
    float4 Position : POSITION0;
    float4 TexCoord : TEXCOORD0;
};

struct VertexShaderOutputCombine
{
    float4 Position : POSITION0;
	float4 TexCoord : TEXCOORD0;
	
};

struct PixelShaderOutput
{
	float4 Color : COLOR0;
};




//////// VERTEX SHADER

VertexShaderOutput VertexShaderBasic(VertexShaderInput input)
{
	VertexShaderOutput output = (VertexShaderOutput)0;
	
	
	output.Position = mul(input.Position, WVP);
	output.Position.w = 1.0;
	//output.PositionWorld = input.Position;
	//output.PositionWorld.w = 1.0;
	//output.SunkPosition = output.Position;
	
    output.TexCoord = input.TexCoord;
	output.Normal = mul(normalize(input.Normal), World);
    output.NormalEye = mul(normalize(input.Normal), NormalMatrix);
	output.Color = DiffuseColor;//input.Color;
	
	return output;
}

VertexShaderOutputCombine VertexShader2D(VertexShaderInputCombine input)
{
	VertexShaderOutputCombine output = (VertexShaderOutputCombine)0;
	
	output.Position = input.Position;
	output.TexCoord = input.TexCoord;

	return output;
}

PixelShaderOutput PixelShader2D(VertexShaderOutputCombine input)
{
	PixelShaderOutput output = (PixelShaderOutput)0;
	output.Color = tex2D(TextureSampler, input.TexCoord);
	output.Color.a *= Alpha;
	return output;
}


VertexShaderOutput VertexShaderRanks(VertexShaderInput input)
{
	VertexShaderOutput output = (VertexShaderOutput)0;
	
	output.Color = DiffuseColor;
	output.Position = mul(input.Position, WVP);
	
    output.TexCoord = input.TexCoord;
    output.NormalEye = mul(normalize(input.Normal), NormalMatrix);
    
    float4x4 world2 = World;
    world2._41 = 0;
    world2._42 = 0;
    world2._43 = 0;
    
    
	output.Normal = mul(input.Normal, world2);
			
	output.PositionWorld = input.Position;
	output.SunkPosition = output.Position;
	return output;
}


VertexShaderOutput VertexShaderGegg(VertexShaderInput input)
{
	VertexShaderOutput output = (VertexShaderOutput)0;
	
	float4 pos = input.Position;
	pos.xy *= sin(input.TexCoord.x*3.14*6+2*sin(input.TexCoord.x*3.14*8+Time*3+GeggStep*10))*(1.0-GeggStep)*0.7+3;
	pos.x += sin(GeggStep*3.14/2)*100.0;
	output.Color = DiffuseColor;
	output.Position = mul(pos, WVP);
	
    output.TexCoord = input.TexCoord;
    output.NormalEye = mul(normalize(input.Normal), NormalMatrix);
    
    float4x4 world2 = World;
    world2._41 = 0;
    world2._42 = 0;
    world2._43 = 0;
        
    
	output.Normal = mul(input.Normal, world2);
			
	output.PositionWorld = input.Position;
	output.SunkPosition = output.Position;
	return output;
}

VertexShaderOutput VertexShaderBananas(VertexShaderInput input)
{
	VertexShaderOutput output = (VertexShaderOutput)0;
	
	output.Color = 1;
	
	float val = tex2Dlod(DisplacementTextureSampler, float4(1.0-input.TexCoord.x-Grow, 0, 0,0)).r;
	
	float4 pos = input.Position + normalize(input.Normal)*val*DisplacementAmount;	
	pos.w = 1;
	output.Position = mul(pos, WVP);
	
    output.TexCoord = input.TexCoord;

    output.NormalEye = mul(normalize(input.Normal), NormalMatrix);
    
    float4x4 world2 = World;
    world2._41 = 0;
    world2._42 = 0;
    world2._43 = 0;
    
    
	output.Normal = mul(input.Normal, world2);
			
	output.PositionWorld = input.Position;
	output.SunkPosition = output.Position;
	return output;
}

///////// LIGHT FUNCTIONS

float diffuselight( float3 normal, float3 lightvec)
{
	normal = normalize(normal);
	lightvec = normalize(lightvec);
	
	return saturate(dot(normal,lightvec)); //dot product between surface and light returns how lit the pixel is. clamp between 0 and 1 because intensity is multiplied later
}

float blinnspecular(float3 normal, float3 lightvec, float3 eyevec, float glossiness)
{
	normal = normalize(normal);
	lightvec = normalize(lightvec);
	eyevec = normalize(eyevec);
	
	float3 halfvector = normalize(eyevec+lightvec); //add eye and light together for half vector (Blinn)
	
	float specular;
	specular = dot(halfvector, normal); //dot between half and normal (Blinn)
	specular = pow(specular, glossiness); //power specular to glossiness to sharpen highlight
	specular *= saturate(dot(normal,lightvec) * 4); //fix for Specular through surface bug. what this does is just make sure no specular happens on unlit parts. the multiplier works as a bias
	
	return specular;
}

float fogamount( float fogDensity, float3 PositionVS )
{
	float z = length( PositionVS ); // viewer position is at origin
	return saturate(pow(2.7182818, -(fogDensity * z/100)*10)*20);
}


///////// PIXEL SHADER

PixelShaderOutput PixelShaderBasic(VertexShaderOutput input)
{
	PixelShaderOutput output;
	
	float3 light1W = mul(Light1Position, View);
	float3 light2W = mul(Light2Position, View);
	float3 color1Light = diffuselight(input.Normal, light1W)*Light1Color*Light1Intensity;
	float3 color2Light = diffuselight(input.Normal, light2W)*Light2Color*Light2Intensity;
	float3 spec1Light = blinnspecular(input.Normal, Light1Position, CameraPosition, Light1SpecularGloss)*SpecularColor*Light1SpecularIntensity;
	float3 spec2Light = blinnspecular(input.Normal, Light2Position, CameraPosition, Light2SpecularGloss)*SpecularColor*Light2SpecularIntensity;
	
	/*float light1Falloff = 1.0/(length(input.PositionWorld-Light1Position)*10);
	color1Light *= light1Falloff;
	
	float light2Falloff = 1.0/(length(Light2Position-input.PositionWorld)*10);
	color2Light *= light2Falloff;*/
	
	float3 diffuse = input.Color;//tex2D(TextureSampler, input.TexCoord);
	
	//float2 ssaoTC = float2(input.ScreenPosition.x, input.ScreenPosition.y);
	//float4 ssaoSample = tex2D(SSAOTextureSampler, ssaoTC);
	
    //float2 ProjectedTexCoords;
	//ProjectedTexCoords[0] = input.Position.x/input.Position.z/2.0f + 0.5f;
    //ProjectedTexCoords[1] = -input.Position.y/input.Position.z/2.0f + 0.5f;
    //float4 caustics = tex2D(TempTextureSampler, ProjectedTexCoords);

	output.Color.rgb = diffuse*(color2Light + color1Light + spec1Light + spec2Light + Ambient) + EmissiveColor;
	float fog = fogamount(Fogginess, input.Position);
	output.Color = output.Color * fog + (1.0-fog) * FogColor;	
	//output.Color = input.Color;
	output.Color.a = 1;
	
	
	return output;
}

PixelShaderOutput PixelShaderTexture(VertexShaderOutput input)
{
	PixelShaderOutput output;
	
	float3 light1W = mul(Light1Position, View);
	float3 light2W = mul(Light2Position, View);
	float3 color1Light = diffuselight(input.Normal, light1W)*Light1Color*Light1Intensity;
	float3 color2Light = diffuselight(input.Normal, light2W)*Light2Color*Light2Intensity;
	float3 spec1Light = blinnspecular(input.Normal, Light1Position, CameraPosition, Light1SpecularGloss)*SpecularColor*Light1SpecularIntensity;
	float3 spec2Light = blinnspecular(input.Normal, Light2Position, CameraPosition, Light2SpecularGloss)*SpecularColor*Light2SpecularIntensity;
	
	/*float light1Falloff = 1.0/(length(input.PositionWorld-Light1Position)*10);
	color1Light *= light1Falloff;
	
	float light2Falloff = 1.0/(length(Light2Position-input.PositionWorld)*10);
	color2Light *= light2Falloff;*/
	
	float4 diffuse = tex2D(TextureSampler, input.TexCoord);

	output.Color.rgb = input.Color*diffuse*(color2Light + color1Light + spec1Light + spec2Light + Ambient) + EmissiveColor;
	//output.Color.rgb = diffuse;
	//float fog = fogamount(Fogginess, input.Position);
	//output.Color = output.Color * fog + (1.0-fog) * FogColor;	
	//output.Color = input.Color;
	output.Color.a = diffuse.a;
	
	
	return output;
}

PixelShaderOutput PixelShaderSimple(VertexShaderOutput input)
{
	PixelShaderOutput output;
	float4 diffuse = tex2D(TextureSampler, input.TexCoord);
	output.Color = diffuse*0.7;
	return output;
}

PixelShaderOutput PixelShaderBasicZ(VertexShaderOutput input)
{
	PixelShaderOutput output;
	float z = input.Position.z/100;
	float3 n = normalize(float3(input.NormalEye.x,input.NormalEye.y,input.NormalEye.z));
	output.Color = float4((n.x+1)/2, 1-((n.y+1)/2), 1-((n.z+1)/2), z);
	
	//output.Color = float4(z,z,z,z);
	return output;
}

//////////// SSAO Stuff
const int passes = 32;
uniform float totStrength = 1.38;
uniform float strength = 0.07;
uniform float offset = 18.0;
uniform float falloff = 0.000002;
uniform float rad = 0.2;
const float invSamples = 1.0/16.0;

static const float3 pSphere[] = {
	float3(-0.010735935, 0.01647018, 0.0062425877),float3(-0.06533369, 0.3647007, -0.13746321),float3(-0.6539235, -0.016726388, -0.53000957),float3(0.40958285, 0.0052428036, -0.5591124),float3(-0.1465366, 0.09899267, 0.15571679),float3(-0.44122112, -0.5458797, 0.04912532),float3(0.03755566, -0.10961345, -0.33040273),float3(0.019100213, 0.29652783, 0.066237666),float3(0.8765323, 0.011236004, 0.28265962),float3(0.29264435, -0.40794238, 0.15964167)
	};

float4 PixelCombineSSAO(VertexShaderOutputCombine input) : COLOR0
{
   // grab a normal for reflecting the sample rays later on
   float3 fres = normalize((tex2D(TextureSamplerNoise,input.TexCoord*offset).xyz*2.0) - float3(1,1,1));
   float4 currentPixelSample = tex2D(TextureSampler, input.TexCoord);
   float currentPixelDepth = currentPixelSample.a;
 
   // current fragment coords in screen space
   float3 ep = float3(input.TexCoord.xy, currentPixelDepth);
   
   // get the normal of current fragment
   float3 norm = currentPixelSample.xyz;
   norm = (norm-0.5)*2;
   //norm.y = 1.0 - norm.y;
 
   float bl = 0.0;
   // adjust for the depth ( not shure if this is good..)
   float radD = rad/currentPixelDepth;
 
   //float3 ray, se, occNorm;
   float occluderDepth, depthDifference;
   float4 occluderFragment;
   float3 ray;
   for(int i=0; i < passes; ++i)
   {
      // get a vector (randomized inside of a sphere with radius 1.0) from a texture and reflect it
      ray = radD*reflect(pSphere[i],fres);
 
      // get the depth of the occluder fragment
      occluderFragment = tex2D(TextureSampler, ep.xy + sign(dot(ray,norm))*ray.xy);
      occluderFragment.xyz = (occluderFragment.xyz-0.5)*2;
   
      // if depthDifference is negative = occluder is behind current fragment
      depthDifference = currentPixelDepth - occluderFragment.a;
 
      // calculate the difference between the normals as a weight
	  // the falloff equation, starts at falloff and is kind of 1/x^2 falling
      bl += 2*step(falloff,depthDifference)*(1.0-dot(occluderFragment.xyz, norm))*(1.0-smoothstep(falloff,strength,depthDifference));
   }
 
   // output the result
   
   //float c = bl/passes;//*invSamples
   float c = 1.0-(totStrength*bl*1.5*invSamples);
   
	//float c = currentPixelDepth;
   return float4(c,c,c,1);
}


int blurSteps = 16;
float blurSize = 0.15;
int cKernelSize = 13;

static const float BlurWeights[] = 
{
	1.0f / 4096.0f,
	12.0f / 4096.0f,
	66.0f / 4096.0f,
	220.0f / 4096.0f,
	495.0f / 4096.0f,
	792.0f / 4096.0f,
	924.0f / 4096.0f,
	792.0f / 4096.0f,
	495.0f / 4096.0f,
	220.0f / 4096.0f,
	66.0f / 4096.0f,
	12.0f / 4096.0f,
	1.0f / 4096.0f,
}; 

float4 HorizontalBlurPS(VertexShaderOutputCombine input) : COLOR0
{
    float4 Color = 0;
    
    for (int i = 0; i < cKernelSize; i++)
    {    
        Color += tex2D(TextureSampler, input.TexCoord + float2((float(i)/cKernelSize-0.5f)*2.0*blurSize, 0)) * BlurWeights[i];
    }

    return Color;
}

float4 VerticalBlurPS(VertexShaderOutputCombine input) : COLOR0
{
    float4 Color = 0;
    
    for (int i = 0; i < cKernelSize; i++)
    {    
        Color += tex2D(TextureSampler, input.TexCoord + float2(0,(float(i)/cKernelSize-0.5f)*2.0*blurSize)) * BlurWeights[i];
    }

    return Color;
}


float4 PixelShader2DMul(VertexShaderOutputCombine input) : COLOR0
{
	float4 t1 = tex2D(TempTextureSampler, input.TexCoord);
	float4 t2 = tex2D(TextureSampler, input.TexCoord);
	float4 output = t1 * t2;
	output.a = t1.a;
    return output;
}

#define VOL_SAMPLES 16
float4 PixelShader2DVolLight(VertexShaderOutputCombine input) : COLOR0
{
	float2 texCoord = input.TexCoord;
	float2 delta = (texCoord - SunPosition.xy);
	delta *= 1.0f / VOL_SAMPLES * Density;
	float3 color = tex2D(TextureSampler, texCoord);
	float lumDecay = 1.0f;
	
	for ( int i = 0; i < VOL_SAMPLES; i++ )
	{
		texCoord -= delta;
		float3 sample = tex2D(TextureSampler, texCoord);
		sample *= lumDecay * Weight;
		color += sample;
		lumDecay *= Decay;
	}

	return float4(color * Exposure, 1);
}



technique BasicShader
{
    pass Pass0
    {
        VertexShader = compile vs_3_0 VertexShaderBasic();
        PixelShader = compile ps_3_0 PixelShaderBasic();
    }
}

technique BasicShaderRanks
{
    pass Pass0
    {
        VertexShader = compile vs_3_0 VertexShaderRanks();
        PixelShader = compile ps_3_0 PixelShaderBasic();
    }
}

technique BasicShaderTexture
{
    pass Pass0
    {
        VertexShader = compile vs_3_0 VertexShaderRanks();
        PixelShader = compile ps_3_0 PixelShaderTexture();
    }
}

technique BasicShaderGegg
{
    pass Pass0
    {
        VertexShader = compile vs_3_0 VertexShaderGegg();
        PixelShader = compile ps_3_0 PixelShaderBasic();
    }
}

technique BasicShaderBananas
{
    pass Pass0
    {
        VertexShader = compile vs_3_0 VertexShaderBananas();
        PixelShader = compile ps_3_0 PixelShaderTexture();
    }
}

technique BasicShaderGeggZ
{
    pass Pass0
    {
        VertexShader = compile vs_3_0 VertexShaderGegg();
        PixelShader = compile ps_3_0 PixelShaderBasicZ();
    }
}

technique BasicShaderRanksZ
{
    pass Pass0
    {
        VertexShader = compile vs_3_0 VertexShaderRanks();
        PixelShader = compile ps_3_0 PixelShaderBasicZ();
    }
}
technique BasicShaderZ
{
    pass Pass0
    {
        VertexShader = compile vs_3_0 VertexShaderRanks();
        PixelShader = compile ps_3_0 PixelShaderBasicZ();
    }
}

technique BasicShaderSSAO
{
    pass Pass0
    {
        VertexShader = compile vs_3_0 VertexShader2D();
        PixelShader = compile ps_3_0 PixelCombineSSAO();
    }
}

technique BasicShaderBlurH
{
    pass Pass0
    {
        VertexShader = compile vs_3_0 VertexShader2D();
        PixelShader = compile ps_3_0 HorizontalBlurPS();
    }
}

technique BasicShaderBlurV
{
    pass Pass0
    {
        VertexShader = compile vs_3_0 VertexShader2D();
        PixelShader = compile ps_3_0 VerticalBlurPS();
    }
}

technique BasicShaderMultiply2D
{
    pass Pass0
    {
        VertexShader = compile vs_3_0 VertexShader2D();
        PixelShader = compile ps_3_0 PixelShader2DMul();
    }
}

technique TwoDee
{
    pass Pass0
    {
        VertexShader = compile vs_3_0 VertexShader2D();
        PixelShader = compile ps_3_0 PixelShader2D();
    }
}

technique VolymetricLight
{
    pass Pass0
    {
        VertexShader = compile vs_3_0 VertexShader2D();
        PixelShader = compile ps_3_0 PixelShader2DVolLight();
    }
}

technique BasicShaderSimple
{
    pass Pass0
    {
        VertexShader = compile vs_3_0 VertexShaderRanks();
        PixelShader = compile ps_3_0 PixelShaderSimple();
    }
}






